import {Container} from '@pixi/display'
import {Text, TextStyle} from '@pixi/text';
import {Point} from "../common/2d/point";
import {GraphicsExt as Graphics} from "../common/graphics-ext";
import {Area} from "../common/2d/area";
import {Chess} from "./chess";
import {ChessModel} from "../model/chess-model";
import {ChessBoardConfig as Config} from "../const";
import {ChessBoardConfig} from "../config/chess-board-config";
import {collect} from "../common/runner-factory";
import {Bounce, gsap} from "gsap";
import {Back, Elastic} from "../common/animation";
import {LinkedList} from "../common/util/linked-node";

export class ChessBoard extends Container {
  private static readonly config: ChessBoardConfig = new ChessBoardConfig(Config);
  private static readonly CHESS_AREA_UP: Area = this.config.chessUpArea;
  private static readonly CHESS_AREA_DOWN: Area = this.config.chessDownArea

  private static CHESS_AREAS = new Map<string, Area>();
  private chessTeams = {};

  private bgLayer: Graphics;
  private chessLayer: Container;
  private effectLayer: Container;

  constructor() {
    super();

    this.initVars();
    this.initLayers();
    this.initChesses();
    this.initEvents();
  }

  static get center(): Point {
    return this.config.center;
  }

  private initLayers() {
    this.bgLayer = ChessBoard.drawBg();
    this.addChild(this.bgLayer)

    this.chessLayer = new Container();
    this.chessLayer.sortableChildren = true;
    this.addChild(this.chessLayer);

    this.effectLayer = new Container();
    this.addChild(this.effectLayer);
  }

  private initVars() {
    this.chessTeams['up'] = new LinkedList<Chess>();
    this.chessTeams['down'] = new LinkedList<Chess>();

    ChessBoard.CHESS_AREAS.set('up', ChessBoard.CHESS_AREA_UP);
    ChessBoard.CHESS_AREAS.set('down', ChessBoard.CHESS_AREA_DOWN);
  }

  private initChesses() {
    const chess = new Chess();
    chess.proxy.model = new ChessModel(10, 10);

    for (let i = 0; i < 7; i++) {
      this.addChess('up', chess.clone());
    }
    const newChess = chess.clone();
    newChess.proxy.model = new ChessModel(999, 10);
    this.addChess('down', newChess);
    // this.addChess('down', chess.clone());

    // console.log(this.chessTeams)
  }

  private initEvents() {
    collect(this, 'afterAttack');
    collect(this, 'chessDie');
    collect(this, 'showDamage');
  }

  private static drawBg(): Graphics
  {
    const bg = new Graphics();
    bg.beginFill(Config.bgColor);
    bg.drawRoundedRect(0, 0, Config.width, Config.height, 50);
    bg.endFill();

    bg.lineStyle({
      width: 10,
      alignment: 0.5,
      color: 0xff0000,
      // cap: LINE_CAP.BUTT,
    });
    bg.drawDash({x:0, y:ChessBoard.center.y}, {x:Config.width, y:ChessBoard.center.y}, 20, 15);

    bg.lineStyle({width:2, color:0xff0000});
    bg.beginFill(0x495634);
    this.CHESS_AREAS.forEach((area) => {
      bg.drawRoundedRectArea(area, 20);
    });
    bg.endFill();

    return bg;
  }

  public addChess(team:string, chess: Chess) {
    chess.team = team;
    const list = this.chessTeams[team] as LinkedList<Chess>;
    list.append(chess);
    this.chessLayer.addChild(chess);
    this.sortChessTeam(team);
  }

  public removeChess(chess: Chess) {
    const chessList = this.chessTeams[chess.team] as LinkedList<Chess>;
    chessList.remove(chess);
    this.chessLayer.removeChild(chess);
    chess.destroy(true);
  }

  private async sortChessTeam(team: string) {
    const chessList = this.chessTeams[team] as LinkedList<Chess>;
    const area = ChessBoard.CHESS_AREAS.get(team);
    if (!area) return;

    const y = area.y + area.height / 2;
    const count = chessList.length;
    const teamWidth = Chess.WIDTH * count + Config.chessMargin * (count - 1);

    const chessArr = chessList.toArray();
    const promises = chessArr.map(async (chess, i) => {
      const x = area.x + (area.width - teamWidth + chess.w) / 2 + i++ * (chess.w + Config.chessMargin);
      await gsap.to(chess, {x, y, duration:.2})
    });
    await Promise.all(promises);
  }

  public async showAttack() {
    const upTeam = this.chessTeams['up'] as LinkedList<Chess>;
    const downTeam = this.chessTeams['down'] as LinkedList<Chess>;

    for (const chess of upTeam.toArray()) {
      await this.sortChessTeam(chess.team)
      await chess.attack(downTeam.head.item);
    }
  }

  public async shake() {
    const x = 20 + 20 * Math.random();
    const y = 20 + 20 * Math.random();
    gsap.from(this, {duration:.2, x, y, ease:Elastic.easeOut})
    // gsap.from(this, {duration:.5, x:20, y:20, ease:Elastic.easeOut})
  }

  private afterAttack() {
    this.shake();
  }

  async chessDie(chess: Chess) {
    // console.log('chessDie======', chess);
    this.removeChess(chess);
  }

  async showDamage(chess:Chess, damage:number) {
    const style = new TextStyle({
      fontFamily: 'Arial',
      fontSize: 45,
      fontWeight: 'bold',
      stroke: '#000000',
      strokeThickness: 5,
      fill: ['#ffffff'],
    })

    const dmgStr = '-' + damage.toString(10);
    const txt = new Text(dmgStr, style);
    txt.x = chess.x;
    txt.y = chess.y;
    txt.alpha = 0.8;
    txt.anchor.set(0.5);
    this.effectLayer.addChild(txt)

    await gsap.from(txt, {alpha:0, width:0, height:0, ease:Back.easeOut})
    await gsap.to(txt, {alpha:0, delay:0.5})
    this.effectLayer.removeChild(txt)
    txt.destroy(true)
  }
}